import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.jpbc.Field;
import it.unisa.dia.gas.jpbc.Pairing;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;

import java.security.MessageDigest;

public class PKIEMain {

	private static String INPUT_PATH = System.getProperty("user.dir")
			+ "/attach/a.properties";// 输入文件
	private static Element mst0, mst1, g1, h, g, gg, g2;
	private static Field G, G2, GT, Zq;
	private Element arra[][] = new Element[10][10];
	private Element arrb[][] = new Element[10][10];
	private Element arraz[][] = new Element[10][10];
	private Element arrbz[][] = new Element[10][10];
	private Element arrr[][] = new Element[10][10];
	private Element arrusk[][][] = new Element[10][3][2];
	private Element arrhsk[][][] = new Element[10][2][2];
	private Element arrpk[] = new Element[4];
	private Element arrCT[] = new Element[3];
	private static Pairing pairing;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// 初始化双线性配对pairing
		pairing = PairingFactory.getPairing(INPUT_PATH);
		// 产生系统参数
		// 产生域G,GT,Zq
		G = pairing.getG1();
		G2 = pairing.getG2();
		GT = pairing.getGT();
		Zq = pairing.getZr();

		int tt = 8;

		Element m = GT.newElementFromBytes("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz".getBytes());
//		Element m = GT.newElementFromBytes("不注册此函数时默认回调函数为到锁回函数可是".getBytes());
		PKIEMain p = new PKIEMain();
		p.KeyGen2();
		int i;
		for (i = 1; i <= tt; i++) {
			p.detaGen2(i);
		}
		for (i = 1; i <= tt; i++) {
			p.Update2(i);
		}
		p.Enc2(tt, m);
		System.out.println("m=" + m);
		try {
			System.out.println("Enc: " + new String(m.toBytes(), "UTF-8"));
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("Dec: " + new String(p.Dec2(tt).toBytes()));
		System.out.println("Dec: " + p.Dec2(tt) + "");
		
//		String s1 = "214D418C6BBAF4B4FBDF628613B682096EB8BFEFA856FB688D6B2E284307FE8995A2EA65761BBAC488EAAFD76A1F6F00E7880815AB7E3B5698CE72943DF43AD9465366A76FC0B8DADD75C8276964E97D31A0FD371C0C08AAC6A6FA1935ACD5994AFD8D229EF7406940826FABE501E6F4D3EDF8371192A04757FEA79B96A03991";
//		String s2 = "01E2C4F8990BFCB8059A4EFE77517B9CE308E4D2745D07D8C6DA50E7379CF57D0FBD2E21D9194F77B86282FFB89A3A100E757FC36095D9BCA6842B793DD2CA4116E001E36977074DD9EB74D77328F656176CDBA1F993704DCC14A862C008597E00491AB244171682FB2D25898F0141CE18FDEAF1196108BD0A9766683B210A52";
//		String s3 = "07E1E41C26278945406FCA83EDA48B30D85D514E36A2F44DB2F4E240A7D60E81C4B7698A18D0B2694EFB0C651EC95E4227D9D989ACAEA76B9DB449543A44DDC95B54FB641ADBA93B4358AA5FB5728E99BA38A196D89DEBD744ABE4B03F0B67E20D2047385BDD3107F77BF80CA0884E3C0D1231F3DADF422398C4487411B2EF1E";
//		String sgg = "6849A70A2F6E19F1FC65CC5145AEC93FD01D813EDD209B5190FD08CBDDF6E522B98B48F0E5180DF38DC0D50F2DB9C2536080942720A329111FACB435C95DBFE562C6445EBD6FE0267C4EF61650528DE1C16BFD60B185E3A97F6C80596AB377029CD9137789994BE8AB2B303A261BA346EB815EDFED80EEEA82CB43408D5710CD";
//		String sa1 = "4814195E7708DB98E188AB63D1BA17137B81B8F7C43D1489F1D44D5787704E1CBA603B02A074621154BAF2C19602FD8F1D4C36A2B2E29E6535D86BE553E4F5A72BEF2A4285AD3FB61B5E59CD165DA959CC0E5C0F39104B9346F30938BE18B1542C38DC7E234D2496EF3AB17A493E642EFBCD9285C33D140D35303FDE88E80EE2";
//		String sb1 = "01333FA6512BC7C990518023E55ABE528332567EF1353C688D70B1EE2B45487613CACA5F4CA407FF3886BFC79CED2B530CE5EB680C88740EFCD65249FEE6F5B25D2F4A74C96E6DFAC2A9CC354AA880F7F7A96B15FB9926DBE7B9F4C07D7C4B71E2392E21B206A3B5D861B921C41E95295109C71B45F536B72AF8FDF7C8F5A258";
//
//		// String s1 =
//		// "786A634A33EDCCD3097812A93ADC5216B01D5F9992105DBB9A40A0325547FB19FF63E3E09D45F9CBEDAE5A3DB41D48BF3F1604E37FF01324C0E89B7B68B55A8C9B394E7E71072BD0075896AA590D6CAA5AF98CFC0F7C95FCBA984F0294CF656406C80F75699949D8EC528853F8844FBC921937FF65F2BB4CB1000FACCC4F4396";
//		// String s2 =
//		// "A3F1160C0F08C1285005A983ACB852EBF311B9D06AAA8CFED95DCCF0DFC36109DF2CF9E6AE692D3817629A0C60F70653E94CCC8BF08B87CE0E2BE37B747DEF9E242BC2A5AD61FFB78BD1966B2F9006BF8426C0DAB2399CACF404892A6B0897C7CC0CC5393A04F09CBE98BD1A08E5A6DB00DC5A3BF85DFB23C3AA2EB95626403E";
//		// String s3 =
//		// "1A191E49F227EC96B98088B34238620F9B9893643DAF98144AB0E39C5F5B39A99558BE8E84C52FA2FD9EE1372955581FFCDF37D2457D97AA287DD945B314EF120F4087B0E24D8393D16F5DE722F10B3DF86BAE0F3BCFF146807263ECA4D8138500C2083A38477164D14DD6889616A3A125D2809F7A5D2B55D2C57031371D423D";
//		// String sgg =
//		// "65631621755A74EE6685A3305D09C3C4978EF37E5BCE8933C958AD3CDB0F76C9136EEA0BA4377B1A4BB58430E6DD3F3FCBD79A3A8E027C0ADF02E68705A934DB72E533AA55A92349F5111C4897B54AB276FD60D756B13454C12D42B114846CCE3856B9039FEFD697ED76BE05997DA0A5B92B9EA8A28F55DDEFE3AB352DE4C5C9";
//		// String sa1 =
//		// "59504C3B24C98D242F55DF0DB409775242877CD2B0244B706B26E80047338C5A6AB68ACDE864772ABD02AA6F90C5FF9A10DFD81A6062A95D4DBC59990B7C246D4A06FFD68B52F06A7839F7DA000E6B4166EC016CF350CFA4F630CCB3E90CB3A03F83E64DF09648F45F1D76A0385B3365EF57B7F64EF155DD851E359735DCDA02";
//		// String sb1 =
//		// "08CEC3BB11D84471C14C8BB11A0A6B0042FE30F26F59C22C5DBD7310FC1A852907605301271CD513B42930D8DE81AC2DC6418BE6CDEBE19C2F8998973FC748F03E6906975C74883E6814B9B1069D4875DC51602F070DC31D9C20E583C2E759B395DA36860D4051D54C186DAA9E9CBE24BBD4AFA04194797E034B807D1984B779";
//		System.out.println("e2  " + Str2Element(GT, s1));
//		System.out.println("e2  " + Str2Element(G, s2));
//		System.out.println("e2  " + Str2Element(G, s3));
//		System.out.println("e2  " + Str2Element(G, sgg));
//		System.out.println("e2  " + Str2Element(G, sa1));
//		System.out.println("e2  " + Str2Element(G, sb1));
//		Element[] a = new Element[3];
//		a[0] = Str2Element(GT, s1);
//		a[1] = Str2Element(G, s2);
//		a[2] = Str2Element(G, s3);
//		Element gg = Str2Element(G, sgg);
//		Element a1 = Str2Element(G, sa1);
//		Element b1 = Str2Element(G, sb1);
//
//		Element e = p.Dec21(tt, a, gg, a1, b1);
//		System.out.println(e);
//		System.out.println("Dec211 " + new String(e.toBytes()));

	}

	public void KeyGen2() {
		// 1.
		Element aa = Zq.newRandomElement().getImmutable();
		Element a0 = Zq.newRandomElement().getImmutable();
		Element a1 = Zq.newRandomElement().getImmutable();
		// 从G中初始化g,g2,h
		g = G.newRandomElement().getImmutable();
		g2 = G.newRandomElement().getImmutable();
		h = G.newRandomElement().getImmutable();
		Element ee = g2.duplicate();
		ee.powZn(aa);
		// 设置gg,g1,mst1,mst0的值
		gg = g2.powZn(aa);
		Element g1_temp = aa.add(a0);
		g1_temp = g1_temp.add(a1);
		g1 = g.duplicate().powZn(g1_temp);
		mst1 = g2.duplicate().powZn(a1);
		mst0 = g2.duplicate().powZn(a0);

		// 2.
		// 计算Z的值
		Element Z = pairing.pairing(g, g);

		// 3.
		// 设置r10,r00,r01的值
		arrr[0][0] = Zq.newRandomElement().getImmutable();
		arrr[0][1] = Zq.newRandomElement().getImmutable();
		arrr[1][0] = Zq.newRandomElement().getImmutable();
		// ----------------------第二步 秘钥产生KeyGen()
		// 产生H(0)，把它存在h0_after_hash中
		Element h0_after_hash = getH(0).duplicate();

		// 产生H(1)，把它存在h1_after_hash中
		Element h1_after_hash = getH(1).duplicate();
		arra[0][0] = mst1
				.duplicate()
				.mul(mst0)
				.mul((g1.duplicate().powZn(h0_after_hash).mul(h))
						.powZn(arrr[1][0].add(arrr[0][0])));
		arrb[0][0] = g.duplicate().powZn(arrr[1][0].add(arrr[0][0]));
		arra[0][1] = mst0.duplicate().mul(
				((g1.duplicate().powZn(h1_after_hash)).mul(h))
						.powZn(arrr[0][1]));
		arrb[0][1] = g.duplicate().powZn(arrr[0][1]);

		// 4.
		arrpk[0] = g.duplicate();
		arrpk[1] = g1.duplicate();
		arrpk[2] = g2.duplicate();
		arrpk[3] = h.duplicate();
		arrusk[0][0][0] = gg.duplicate();
		arrusk[0][1][0] = arra[0][0].duplicate();
		arrusk[0][1][1] = arrb[0][0].duplicate();
		arrusk[0][2][0] = arra[0][1].duplicate();
		arrusk[0][2][1] = arrb[0][1].duplicate();
	}

	public void detaGen2(int t) {
		// 1.
		int i = t % 2;
		arrr[i][t] = Zq.newRandomElement().getImmutable();
		arrr[i][t + 1] = Zq.newRandomElement().getImmutable();

		// 2.
		arraz[i][t] = getmst(i).duplicate().mul(
				(g1.duplicate().powZn(getH(t)).mul(h)).powZn(arrr[i][t]));
		arrbz[i][t] = g.duplicate().powZn(arrr[i][t]);
		arraz[i][t + 1] = getmst(i).duplicate().mul(
				(g1.duplicate().powZn(getH(t + 1)).duplicate().mul(h))
						.powZn(arrr[i][t + 1]));
		arrbz[i][t + 1] = g.duplicate().powZn(arrr[i][t + 1]);

		// 3.
		arrhsk[t][0][0] = arraz[i][t].duplicate();
		arrhsk[t][0][1] = arrbz[i][t].duplicate();
		arrhsk[t][1][0] = arraz[i][t + 1].duplicate();
		arrhsk[t][1][1] = arrbz[i][t + 1].duplicate();
	}

	public void Update2(int t) {
		// 1.
		int i = t % 2;
		int j = (t - 1) % 2;

		// 2.
		gg = arrusk[t - 1][0][0].duplicate();
		arra[j][t - 1] = arrusk[t - 1][1][0].duplicate();
		arrb[j][t - 1] = arrusk[t - 1][1][1].duplicate();
		arra[j][t] = arrusk[t - 1][2][0].duplicate();
		arrb[j][t] = arrusk[t - 1][2][1].duplicate();

		arraz[i][t] = arrhsk[t][0][0].duplicate();
		arrbz[i][t] = arrhsk[t][0][1].duplicate();
		arraz[i][t + 1] = arrhsk[t][1][0].duplicate();
		arrbz[i][t + 1] = arrhsk[t][1][1].duplicate();

		// 3.
		arra[i][t] = arra[j][t].duplicate().mul(arraz[i][t]).duplicate();
		arrb[i][t] = arrb[j][t].duplicate().mul(arrbz[i][t]).duplicate();
		arra[i][t + 1] = arraz[i][t + 1].duplicate();
		arrb[i][t + 1] = arrbz[i][t + 1].duplicate();

		// 4.
		arrusk[t][0][0] = gg.duplicate();
		arrusk[t][1][0] = arra[i][t].duplicate();
		arrusk[t][1][1] = arrb[i][t].duplicate();
		arrusk[t][2][0] = arra[i][t + 1].duplicate();
		arrusk[t][2][1] = arrb[i][t + 1].duplicate();
	}

	public void Enc2(int t, Element m) {
		// 1.
		Element s = Zq.newRandomElement().getImmutable();
		Element C1 = (pairing.pairing(g1, g2)).duplicate().powZn(s).mul(m)
				.getImmutable();
		Element C2 = g.duplicate().powZn(s).getImmutable();
		Element C3 = (g1.duplicate().powZn(getH(t)).mul(h)).powZn(s)
				.getImmutable();

		// 2.
		arrCT[0] = C1;
		arrCT[1] = C2;
		arrCT[2] = C3;
		System.out.println("C1=" + C1 + "\nC2=" + C2 + "\nC3=" + C3);
	}

	public Element Dec2(int t) {
		// 1.
		int i = t % 2;
		Element C1 = arrCT[0].duplicate();
		Element C2 = arrCT[1].duplicate();
		Element C3 = arrCT[2].duplicate();

		// 2.
		gg = arrusk[t][0][0];
		arra[i][t] = arrusk[t][1][0].duplicate();
		arrb[i][t] = arrusk[t][1][1].duplicate();
		arra[i][t + 1] = arrusk[t][2][0].duplicate();
		arrb[i][t + 1] = arrusk[t][2][1].duplicate();

		Element m = (C1.duplicate().mul(pairing.pairing(C3, arrb[i][t])))
				.div((pairing.pairing(C2, gg.duplicate().mul(arra[i][t]))));
		return m;
	}

	public Element Dec21(int t, Element arrCT[], Element gg, Element a1,
			Element b1) {
		// 1.
		int i = t % 2;
		Element C1 = arrCT[0].duplicate();
		Element C2 = arrCT[1].duplicate();
		Element C3 = arrCT[2].duplicate();

		// // 2.
		// gg = arrusk[t][0][0];
		// arra[i][t] = arrusk[t][1][0].duplicate();
		// arrb[i][t] = arrusk[t][1][1].duplicate();
		// arra[i][t + 1] = arrusk[t][2][0].duplicate();
		// arrb[i][t + 1] = arrusk[t][2][1].duplicate();
		//
		// Element m = (C1.duplicate().mul(pairing.pairing(C3,
		// a1))).div((pairing
		// .pairing(C2, gg.duplicate().mul(b1))));
		// gg = gg;
		arra[i][t] = a1;
		arrb[i][t] = b1;

		Element m = (C1.duplicate().mul(pairing.pairing(C3, arrb[i][t])))
				.div((pairing.pairing(C2, gg.duplicate().mul(arra[i][t]))));
		// Element m = (C1.duplicate().mul(pairing.pairing(C3,
		// a1))).div((pairing
		// .pairing(C2, gg.duplicate().mul(b1))));
		return m;
	}

	public static Element getmst(int t) {
		return t % 2 == 0 ? mst0 : mst1;
	}

	public static Element getH(int t1) {
		byte[] h1_before_hash = intToBytes(t1);
		Element h1_after_hash = Zq.newZeroElement();// 随机数h1_after_hash
		try {
			MessageDigest digest = MessageDigest.getInstance("MD5");
			digest.update(h1_before_hash);
			byte[] dig = digest.digest();
			h1_after_hash.setFromHash(dig, 0, dig.length);// hash处理后存放到h里面
		} catch (Exception e) {
		}
		return h1_after_hash;
	}

	/**
	 * 将int类型的数据转换为byte数组 原理：将int数据中的四个byte取出，分别存储
	 */
	public static byte[] intToBytes(int n) {
		byte[] b = new byte[4];
		for (int i = 0; i < 4; i++) {
			b[i] = (byte) (n >> (24 - i * 8));
		}
		return b;
	}

	public static String Element2Str(Element e) {
		byte[] cc1 = e.toBytes();
		String scc1 = StringUtil.byte2HexStr(cc1);
		return scc1;
	}

	public static Element Str2Element(Field GGG, String s) {
		byte[] rcc1 = StringUtil.hexStringToBytes(s);
		Element e21 = GGG.newElementFromHash(rcc1, 0, rcc1.length);
		e21.setFromBytes(rcc1);
		return e21;
	}
}
/*
 * 参考: http://blog.sina.com.cn/s/blog_812973c30101dez3.html
 * 
 * http://blog.csdn.net/liuweiran900217/article/details/21819637
 */
